import java.util.ArrayList;
import java.util.Random;


public class LongHaarFilter {
	public static final int filterSize = 23;
	public static final int[] initialFilter = {1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1};
	public static final int numberofRows = 28;
	public static final int numberofColumns = 23;
	
	private ArrayList<int[]> filters;
	private double variance;
	private double covariance;
	private ArrayList<TrainingImage> trainingImages;
	private MeanImage meanImage;
	
	public LongHaarFilter(ArrayList<TrainingImage> trImgs, MeanImage meanImg){
		this.trainingImages = trImgs;
		this.meanImage = meanImg;
		this.filters = new ArrayList<int[]>(); 
		for (int i = 0; i < numberofRows ; i++) {
			this.filters.add(initialFilter);
		}
		
	}

	public ArrayList<int[]> getFilters() {
		return filters;
	}

	public void setFilters(ArrayList<int[]> filters) {
		this.filters = filters;
	}

	public double getVariance() {
		return variance;
	}

	public void setVariance(double variance) {
		this.variance = variance;
	}

	public double getCovariance() {
		return covariance;
	}

	public void setCovariance(double covariance) {
		this.covariance = covariance;
	}

	public ArrayList<TrainingImage> getTrainingImages() {
		return trainingImages;
	}

	public void setTrainingImages(ArrayList<TrainingImage> trainingImages) {
		this.trainingImages = trainingImages;
	}

	public MeanImage getMeanImage() {
		return meanImage;
	}

	public void setMeanImage(MeanImage meanImage) {
		this.meanImage = meanImage;
	}

	private double _computeVariance(int[] filterI, int rowIndex) {
		this.covariance = _computeCovariance(filterI, rowIndex);
		double variance = 0.0;
		double varianceValue = 0.0;
		for (int j = 0; j < numberofColumns; j++) {
			double varianceJValue = filterI[j] * filterI[j];
			varianceValue += varianceJValue;
		}
		varianceValue = varianceValue * this.covariance;
		variance = varianceValue;
		
		return variance;
	}

	private double _computeCovariance(int[] filterI, int rowIndex) {
		double covariance = 0.0;
		double covarianceValue = 0.0;
		for (TrainingImage trImage : this.trainingImages) {
			for (int j = 0; j < numberofColumns; j++) {
				double imgValueMinusMeanValue = trImage.getLowFrequencyImage()[rowIndex][j] - meanImage.getMeanImage()[rowIndex][j];
				covarianceValue += imgValueMinusMeanValue * imgValueMinusMeanValue;
			}
		}
		covarianceValue = covarianceValue / (double)this.trainingImages.size();
		covariance = covarianceValue;
		
		
		return covariance;
		
	}
	
	public ArrayList<int[]> OptimizeFilterList(ArrayList<int[]> filterLists){
		for (int i = 0; i < filterLists.size(); i++) {
			filterLists.set(i, _OptimizeFilter(filterLists.get(i), i));
		}
		
		return filterLists;
	}
	
	private int[] _OptimizeFilter(int[] filter, int rowIndex){
		int numberOfIteraions = 10000;
		double  rouletteChance = 0.1;
		Random r = new Random();
		int[] newFilter = new int[filter.length];
		for (int k = 0; k < filter.length; k++) {
			newFilter[k] = filter[k];
		}
		for (int i = 0; i < numberOfIteraions; i++) {
			for (int j = 0; j < filter.length; j++) {
				double rouletteResult = r.nextDouble();
				if(rouletteResult <= rouletteChance){
					if(newFilter[j] == 1)
						newFilter[j] = -1;
					else
						newFilter[j] = 1;
				}
			}
			double oldFilterFitness = _EvaluateFitness(filter, rowIndex);
			double newFilterFitness = _EvaluateFitness(newFilter, rowIndex);
			if(newFilterFitness < oldFilterFitness)
				newFilter = filter;
		}
		
		return newFilter;
	}
	
	private double _EvaluateFitness(int[] filter, int rowIndex){
		double K1 = 500;
		double variance = _computeVariance(filter, rowIndex);
		double k1PenaltyValue = 0.0;
		
		for (int i : filter) {
			k1PenaltyValue += i;
		}
		k1PenaltyValue = Math.pow(k1PenaltyValue, 2);
		k1PenaltyValue = (k1PenaltyValue * K1) / 2.0;
		
		double fitness = ((-1)*variance) + k1PenaltyValue;
		
		return fitness;
	}
	
	
}
